/*********************************************************************************
 *      Copyright:  (C) 2025 LingYun<iot25@lingyun>
 *                  All rights reserved.
 *
 *       Filename:  fork_exec.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(10/03/25)
 *         Author:  LingYun <iot25@lingyun>
 *      ChangeLog:  1, Release initial version on "10/03/25 19:32:00"
 *                 
 ********************************************************************************/
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<ctype.h>
// 标准输出重定向的文件, /tmp路径是在Linux系统在内存里做的一个文件系统，放在这里不用写硬盘程序运行会快些。
#define  TMP_FILE      "/tmp/.ifconfig.log"
int main(int argc, char *argv[])
{
	pid_t         			pid;
	int                     fd = -1;
	char               		buf[1024];
	int						rv= -1;
	char				   *ptr;
	FILE				   *fp;
	char                   *ip_start;
	char                   *ip_end;
	char                    ipaddr[16];
    // 父进程打开这个文件，子进程会继承父进程打开的这个文件描述符，这样父子进程都可以通过各自的文件描述符访问同一个文件了。
	if( (fd=open(TMP_FILE, O_RDWR|O_CREAT|O_TRUNC,0644))<0)
	{
		printf(" Redirect standard output to file failure:%s\n",strerror(errno));//将标准输出重定向到文件失败
		return -1;
	}
    
	//父进程开始创建进程。
	pid=fork();
	if(pid<0)
	{
		printf(" fork() create child process failure: %s\n", strerror(errno));//创建子进程过程失败
		return -1;
	}
	else if(pid ==0)//子进程开始运行。
    {
		printf(" Child process start execut ifconfig program\n");//
        //子进程会继承父进程打开的文件描述符，此时子进程重定向标准输出父进程所打开的文件里
	    dup2(fd, STDOUT_FILENO);
		/*
        	下面这句execl(...)函数是让子进程开始执行带参数的ifconfig命令： ifconfig eth0execl()会导致子进程彻底丢掉父进程的文本段、数据段，并加载/sbin/ifconfig这个程序的文本段、数据段
重新建立进程内存空间。
		  	execl()函数的第一个参数是所要执行程序的路径，ifconfig命令(程序)的路径是/sbin/ifconfig；接下来的参数是命令及其相关选项、参数，每个命令、选项、参数都用双引号("")扩起来，并以NULL结束。
 		*/

 		/*
 		ifconfig eth0命令在执行时会将命令的执行的结果输出到标准输出上，而这时子进程已经重定向标准输出到文件中去了,所以ifconfig命令的打印结果会输出到文件中去，这样父进程就会从该文件里读到子进程执行该命令的结果；
 		*/


		execl("/sbin/ifconfig","ifconfig", "eth0",NULL);
		/* execl()函数并不会返回，因为他去执行另外一个程序了。如果execl()返回了，说明该系统调用出错了。 */
		printf(" Child process execute another program ,will not return here.Return here means execl() error\n");
		return -1;
	}
	else
	{
		//父进程等待3秒，让子进程先执行
		sleep(3);
	}
	//子进程因为调用了execl(),他会丢失父进程的文本，所以子进程不会执行到这里了。只有父进程会继续执行后面的代码。
	memset(buf,0 ,sizeof(buf));

	// 父进程这时候读是读不到内容的，这时因为子进程往文件里写内容时已经将文件偏移量修改到文件尾了
	rv=read(fd, buf, sizeof(buf));
	printf("Read %d bytes data directly read after child process write\n",rv);//读取子进程写入后直接读取%d字节数据

	// 父进程如果要读则需要将文件偏移量设置到文件头才能读到内容
	memset(buf,0, sizeof(buf));//清空缓冲区
	lseek(fd, 0,SEEK_SET);//将文件指针移动到文件开头
	rv=read(fd,buf,sizeof(buf));
	printf(" Read %d bytes data after lseek:\n %s", rv, buf);


	fp= fdopen(fd,"r");

	fseek(fp, 0, SEEK_SET);// 重新设置文件偏移量到文件头
	while( fgets(buf,sizeof(buf),fp) )
	{
		

		if( strstr (buf, "netmask") )
		{

		
			ptr = strstr(buf, "inet");// 查找"inet"关键字，inet关键字后面跟的就是IP地址；
			if( !ptr)
			{
				break;
			}
			ptr += strlen("inet");

			while( isblank(*ptr))// inet 关键字后面是空白符，我们不确定是空格还是TAB，所以这里使用isblank()函数判断如果字符还是空白符就往后跳过；
				ptr++;

			ip_start = ptr;// 跳过空白符后跟着的就是IP地址的起始字符；

			while( !isblank(*ptr))
				ptr++;

			ip_end = ptr;//// 第一个空白字符的地址也就是IP地址终止的字符位置.

			memset(ipaddr,0 , sizeof(ipaddr));/* 使用memcpy()函数将IP地址拷贝到存放IP地址的buffer中，其中ip_end-ip_start就是IP地址的长
度，ip_start就是IP地址的起始位置；*/
			memcpy(ipaddr, ip_start, ip_end-ip_start);

			break;
		
		}
		
    }
   	printf(" Parser and get  IP address: %s\n", ipaddr);//解析器和获取IP地址.

	fclose(fp);
	unlink(TMP_FILE);
	
	return 0;
}

